Skip to content

Conversation

YonahGoldberg
Copy link
Contributor

@YonahGoldberg YonahGoldberg commented Aug 6, 2025

Add -opt-bisect-skip=<idx> option for skipping pass at index <idx> in the pipeline.

OptBisect allows you to binary search for a pass causing some error. It is useful as a next step to disable that pass as a sanity check and to see if the issue is isolated to one pass.

@llvmbot llvmbot added the llvm:ir label Aug 6, 2025
@YonahGoldberg
Copy link
Contributor Author

@AlexMaclean @nikic

@llvmbot
Copy link
Member

llvmbot commented Aug 6, 2025

@llvm/pr-subscribers-llvm-support

@llvm/pr-subscribers-llvm-ir

Author: Yonah Goldberg (YonahGoldberg)

Changes

Add -opt-bisect-skip=<idx> option for skipping pass at index <idx> in the pipeline.

OptBisect allows you to binary search for a pass causing some error. It is useful as a next step to disable that pass as a sanity check and to see if the issue is isolated to one pass.


Full diff: https://github.com/llvm/llvm-project/pull/152393.diff

3 Files Affected:

  • (modified) llvm/include/llvm/IR/OptBisect.h (+9-1)
  • (modified) llvm/lib/IR/OptBisect.cpp (+8-1)
  • (added) llvm/test/Other/opt-bisect-skip.ll (+15)
diff --git a/llvm/include/llvm/IR/OptBisect.h b/llvm/include/llvm/IR/OptBisect.h
index d813ae933d65e..9dcc0077552d4 100644
--- a/llvm/include/llvm/IR/OptBisect.h
+++ b/llvm/include/llvm/IR/OptBisect.h
@@ -14,6 +14,7 @@
 #ifndef LLVM_IR_OPTBISECT_H
 #define LLVM_IR_OPTBISECT_H
 
+#include "llvm/ADT/SmallSet.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/ADT/StringSet.h"
 #include "llvm/Support/Compiler.h"
@@ -67,7 +68,13 @@ class LLVM_ABI OptBisect : public OptPassGate {
                      StringRef IRDescription) const override;
 
   /// isEnabled() should return true before calling shouldRunPass().
-  bool isEnabled() const override { return BisectLimit != Disabled; }
+  bool isEnabled() const override {
+    return BisectLimit != Disabled || !BisectSkipNumbers.empty();
+  }
+
+  /// Add pass at index SkipNumber to the list of passes to skip
+  /// during bisection.
+  void addSkip(int SkipNumber) { BisectSkipNumbers.insert(SkipNumber); }
 
   /// Set the new optimization limit and reset the counter. Passing
   /// OptBisect::Disabled disables the limiting.
@@ -81,6 +88,7 @@ class LLVM_ABI OptBisect : public OptPassGate {
 private:
   int BisectLimit = Disabled;
   mutable int LastBisectNum = 0;
+  SmallSet<int, 4> BisectSkipNumbers;
 };
 
 /// This class implements a mechanism to disable passes and individual
diff --git a/llvm/lib/IR/OptBisect.cpp b/llvm/lib/IR/OptBisect.cpp
index 29ca268408265..defb8a98f9716 100644
--- a/llvm/lib/IR/OptBisect.cpp
+++ b/llvm/lib/IR/OptBisect.cpp
@@ -37,6 +37,11 @@ static cl::opt<int> OptBisectLimit("opt-bisect-limit", cl::Hidden,
                                    }),
                                    cl::desc("Maximum optimization to perform"));
 
+static cl::opt<int> OptBisectSkip(
+    "opt-bisect-skip", cl::Hidden, cl::init(OptBisect::Disabled), cl::Optional,
+    cl::cb<void, int>([](int PassNum) { getOptBisector().addSkip(PassNum); }),
+    cl::desc("Skip pass at the given index in the optimization pipeline"));
+
 static cl::opt<bool> OptBisectVerbose(
     "opt-bisect-verbose",
     cl::desc("Show verbose output when opt-bisect-limit is set"), cl::Hidden,
@@ -66,7 +71,9 @@ bool OptBisect::shouldRunPass(StringRef PassName,
   assert(isEnabled());
 
   int CurBisectNum = ++LastBisectNum;
-  bool ShouldRun = (BisectLimit == -1 || CurBisectNum <= BisectLimit);
+  bool ShouldRun = (BisectLimit == -1 || BisectLimit == Disabled ||
+                    CurBisectNum <= BisectLimit) &&
+                   !BisectSkipNumbers.contains(CurBisectNum);
   if (OptBisectVerbose)
     printPassMessage(PassName, CurBisectNum, IRDescription, ShouldRun);
   return ShouldRun;
diff --git a/llvm/test/Other/opt-bisect-skip.ll b/llvm/test/Other/opt-bisect-skip.ll
new file mode 100644
index 0000000000000..da94ad6230527
--- /dev/null
+++ b/llvm/test/Other/opt-bisect-skip.ll
@@ -0,0 +1,15 @@
+; Test that verifies functionality for -opt-bisect-skip
+
+; RUN: opt -O1 -opt-bisect-skip=3 -opt-bisect-skip=7 %s 2>&1 | FileCheck %s --check-prefix=CHECK-DISABLE-PASS
+; CHECK-DISABLE-PASS: BISECT: running pass (1) annotation2metadata on [module]
+; CHECK-DISABLE-PASS: BISECT: running pass (2) forceattrs on [module]
+; CHECK-DISABLE-PASS: BISECT: NOT running pass (3) inferattrs on [module]
+; CHECK-DISABLE-PASS: BISECT: running pass (4) lower-expect on foo
+; CHECK-DISABLE-PASS: BISECT: running pass (5) simplifycfg on foo
+; CHECK-DISABLE-PASS: BISECT: running pass (6) sroa on foo
+; CHECK-DISABLE-PASS: BISECT: NOT running pass (7) early-cse on foo
+; CHECK-DISABLE-PASS: BISECT: running pass (8) openmp-opt on [module]
+
+define void @foo() {
+  ret void
+}
\ No newline at end of file

Copy link
Member

@AlexMaclean AlexMaclean left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, I personally use this feature in our down-stream fork and find it to be a nice complement to --opt-bisect-limit when investigating correctness issues. Please wait for some more review prior to landing.

@github-actions
Copy link

github-actions bot commented Aug 20, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

Copy link
Contributor

@nikic nikic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be better to make opt-bisect work the same way that debug-counter works nowadays, that is by specifying a list of ranges. So that you can use something like -opt-bisect=1-1000 to get something similar to the current opt-bisect-limit, but also -opt-bisect=1-10,20-30 to skip a certain range of passes, etc.

@YonahGoldberg YonahGoldberg changed the title [OptBisect] Add opt-bisect-skip option for skipping passes during bisection [OptBisect] Add support for selecting ranges of passes and refactor DebugCounter to use a shared Range API. Sep 4, 2025
@YonahGoldberg
Copy link
Contributor Author

@Artem-B @nikic when you have a chance please look over the newest version of this. Here are the updates:

  • With OptBisect you can now do -opt-bisect=1-10,20-30 like Nikita asked. -opt-bisect-limit=N just translates to -opt-bisect=1-N
  • Since OptBisect and DebugCounter now have the same range based command line semantics, I refactored the support for range parsing into a separate utility in LLVMSupport.
  • DebugCounter supported ranges, but there was no lit-test to test this functionality, so I added one.

@YonahGoldberg
Copy link
Contributor Author

Hey @nikic I think I addressed all your comments, thanks for the feedback :)

The most major change is I decided to make Range into a class to preserve the Begin <= End invariant.

@YonahGoldberg
Copy link
Contributor Author

@nikic @Artem-B gentle ping for review.

@nikic nikic requested a review from kuhar September 19, 2025 10:02
Copy link
Contributor

@nikic nikic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks reasonable to me, but an ADT reviewer will probably want to take a look.

I have one high level question: Why is the Range here signed? It seems like in this context negative numbers are not relevant. Is this just because you expect it to be useful for other use cases? (Worth noting that for "real" ranges we have ConstantRange and ConstantRangeList in IR.)

@YonahGoldberg
Copy link
Contributor Author

This looks reasonable to me, but an ADT reviewer will probably want to take a look.

I have one high level question: Why is the Range here signed? It seems like in this context negative numbers are not relevant. Is this just because you expect it to be useful for other use cases? (Worth noting that for "real" ranges we have ConstantRange and ConstantRangeList in IR.)

It's signed because I expect it might be useful in other cases, yes.

Do you think it would make more sense to just extend ConstantRangeList? I could create a RangeList::fromRangeString function. I don't actually need the range bounds to be mutable so I think this actually might make the most sense.

CC: @arsenm @haopliu

@YonahGoldberg YonahGoldberg requested a review from kuhar September 23, 2025 21:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants